package flexo.graphic;

import java.util.HashMap;

import org.lwjgl.opengl.*;

import flexo.common.LogMsgType;

public class Material extends flexo.common.ReferenceCount
{
	public enum Blending
	{
		SOLID,
		TRANSPARENT_MASK,		
		TRANSPARENT_ALPHA,
		TRANSPARENT_ADD,
		TRANSPARENT_MULTIPLY
	}
	
	protected String name;
	protected boolean faceCull;
	protected boolean wireframe;
	protected Blending blending;
	protected Shader shader;
	protected HashMap<String,ShaderParam> params;
	protected boolean ccw;
	
	public void setReversePolygons(boolean enable)
	{
		this.ccw = !enable;
	}
	
	public String getName()
	{
		return name;
	}
	
	public void setFaceCulling(boolean enabled)
	{
		faceCull = enabled;
	}
	
	public void setWireframe(boolean enabled)
	{
		wireframe = enabled;
	}
	
	Material(String name, String shaderName)
	{
		init(name, shaderName);
	}
	
	protected void init(String name, String shaderName)
	{
		flexo.Kernel.log("flexo.graphic.Manager", "Creating material '"+name+"'");
		this.name = name;
		flexo.graphic.GraphicSystem gs = (flexo.graphic.GraphicSystem)flexo.Kernel.getSystem(flexo.graphic.GraphicSystem.ID);
		shader = gs.shaderPool.get(shaderName);
		shader.grab();
		blending = Blending.SOLID;
		params = new HashMap<String, ShaderParam>();
		setFaceCulling(true);
		ccw = true;
		
		// We create all the shader parameters
		for (Shader.ShaderParamInfo spi : shader.params)
		{
			ShaderParam.Type type = ShaderParam.Type.INT;
			
			if (spi.type.equals("int"))
			{
				type = ShaderParam.Type.INT;
			}
			else if (spi.type.equals("float"))
			{
				type = ShaderParam.Type.FLOAT;
			}
			else if (spi.type.equals("vec3"))
			{
				type = ShaderParam.Type.VECTOR;
			}
			else if (spi.type.equals("vec4"))
			{
				type = ShaderParam.Type.COLOR;
			}
			else if (spi.type.equals("mat4"))
			{
				type = ShaderParam.Type.MATRIX;
			}
			else if (spi.type.substring(0,7).equals("sampler"))
			{
				type = ShaderParam.Type.TEXTURE;
			}
			
			ShaderParam sp = new ShaderParam(spi.name,type);
			
			params.put(sp.name, sp);
		}
	}
	
	public void destroy()
	{
		shader.remove();
		flexo.graphic.GraphicSystem gs = (flexo.graphic.GraphicSystem)flexo.Kernel.getSystem(flexo.graphic.GraphicSystem.ID);
		gs.materialPool.remove(this);
		
		for (ShaderParam p: params.values())
		{
			p.remove();
		}
	}
	
	public ShaderParam getShaderParam(String name)
	{
		ShaderParam p = params.get(name);
		
		if (p == null)
		{
			flexo.Kernel.log("flexo.graphic.Material", "'"+name+"' is not a parameter of shader '"+shader.getName()+"'", LogMsgType.WARNING);
		}
		
		return p;
	}
	
	public void setBlending(Blending b)
	{
		this.blending = b;
	}
	
	public void bind()
	{
		if (ccw)
		{
			GL11.glFrontFace(GL11.GL_CCW);
		}
		else
		{
			GL11.glFrontFace(GL11.GL_CW);
		}
		
		if (faceCull)
		{
			GL11.glEnable(GL11.GL_CULL_FACE);
		}
		else
		{
			GL11.glDisable(GL11.GL_CULL_FACE);
		}
		
		if (wireframe)
		{
			GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK,GL11.GL_LINE);
		}
		else
		{
			GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK,GL11.GL_FILL);
		}
		
		// Shader activation
		shader.bind();
		
		// Shader parameter update
		for (ShaderParam p: params.values())
		{
			p.apply(shader);
		}
	}
}